package bancosys.tec.persist.spring.transaction;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import jmine.tec.utils.debug.impl.DebugRuntimeFactory;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.jdbc.ConnectionWrapper;
import org.springframework.orm.hibernate3.HibernateTransactionManager;
import org.springframework.transaction.TransactionDefinition;

import bancosys.tec.persist.exception.HibernateExtensionException;
import bancosys.tec.persist.hibernate.PartitionSessionFactory;

/**
 * {@link HibernateTransactionManager} para {@link PartitionSessionFactory} que seta a particao correta.
 * 
 * @author takeshi
 * @deprecated usar {@link PartitionTransactionManagerDecorator} junto com um {@link HibernateTransactionManager}
 */
public class PartitionTransactionManager extends HibernateTransactionManager {

    private static final Log LOG = LogFactory.getLog(PartitionTransactionManager.class);

    private static final Map<Object, String> OPEN_TRANSACTIONS = new ConcurrentHashMap<Object, String>();;

    /**
     * {@inheritDoc}
     */
    @Override
    protected void doBegin(Object transaction, TransactionDefinition definition) {
        ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
        ClassLoader wrapperClassLoader = ConnectionWrapper.class.getClassLoader();
        if(!wrapperClassLoader.equals(currentClassLoader) && currentClassLoader != null) {
            Thread.currentThread().setContextClassLoader(wrapperClassLoader);
        }
        try {
            if (definition instanceof PartitionTransactionDefinition) {
                PartitionTransactionDefinition partitionDefinition = (PartitionTransactionDefinition) definition;
                if (this.getSessionFactory() instanceof PartitionSessionFactory) {
                    PartitionSessionFactory partitionSessionFactory = (PartitionSessionFactory) this.getSessionFactory();
                    partitionSessionFactory.setThreadPartition(partitionDefinition.getPartition());
                    partitionSessionFactory.setThreadPureSessionOnly(partitionDefinition.isPureSessionOnly());
                } else {
                    LOG.error("Não foi possível inicializar transação pois um PartitionTransactionManger necessita de um"
                            + "PartitionSessionFactory para funcionar. Verifique a configuração do spring.");
                    throw new HibernateExtensionException("persist.unableToInitializeTransaction");
                }
            }
            super.doBegin(transaction, definition);
            if (DebugRuntimeFactory.getInstance().active()) {
                OPEN_TRANSACTIONS.put(transaction, DebugRuntimeFactory.getCurrentThreadStack());
            }
        } finally {
            if(!wrapperClassLoader.equals(currentClassLoader) && currentClassLoader != null) {
                Thread.currentThread().setContextClassLoader(currentClassLoader);
            }
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.springframework.orm.hibernate3.HibernateTransactionManager#doCleanupAfterCompletion(java.lang.Object)
     */
    @Override
    protected void doCleanupAfterCompletion(Object transaction) {
        ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
        ClassLoader wrapperClassLoader = ConnectionWrapper.class.getClassLoader();
        if(!wrapperClassLoader.equals(currentClassLoader) && currentClassLoader != null) {
            Thread.currentThread().setContextClassLoader(wrapperClassLoader);
        }
        try {
            super.doCleanupAfterCompletion(transaction);
            if (DebugRuntimeFactory.getInstance().active()) {
                OPEN_TRANSACTIONS.remove(transaction);
            }
        } finally {
            if(!wrapperClassLoader.equals(currentClassLoader) && currentClassLoader != null) {
                Thread.currentThread().setContextClassLoader(currentClassLoader);
            }
        }
    }

    /**
     * Devolve as informacoes sobre as transacoes abertas
     * 
     * @return array de String
     */
    public String[] getOpenTransactionsAsArray() {
        return OPEN_TRANSACTIONS.values().toArray(new String[OPEN_TRANSACTIONS.size()]);
    }

    /**
     * @return the open transactions
     */
    public static Map<Object, String> getOpenTransactions() {
        return OPEN_TRANSACTIONS;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void afterPropertiesSet() {
        super.afterPropertiesSet();
        DebugRuntimeFactory.getInstance().exposeAsJMX("partitionTransactionManager", this);
    }

}
